unit stacktrace2;

{$MODE Delphi}

interface

uses LCLIntf, sysutils, classes, symbolhandler, CEFuncProc, NewKernelHandler,
  byteinterpreter, commonTypeDefs;

procedure seperatestacktraceline(s: string; var address: string; var bytes: string; var details: string);
procedure ce_stacktrace(esp: ptrUint; ebp: ptrUint; eip: ptrUint; stack: Pbytearray; sizeinbytes: integer; trace: tstrings; force4byteblocks: boolean=true; showmodulesonly: boolean=false; nosystemmodules:boolean=false; maxdepth:integer=0; referenceaddress: ptruint=0; referencename:string='');

implementation

uses StrUtils, ProcessHandlerUnit;

procedure seperatestacktraceline(s: string; var address: string; var bytes: string; var details: string);
var i,j: integer;
begin
  //has 2 ' - ' occurances
  i:=pos(' - ',s);
  address:=copy(s,1,i-1);

  j:=posex(' - ',s,i+3);
  bytes:=copy(s,i+3,j-(i+3));

  details:=copy(s,j+3,length(s)); //leftover
end;

procedure ce_stacktrace(esp: ptrUint; ebp: ptrUint; eip: ptrUint; stack: Pbytearray; sizeinbytes: integer; trace: tstrings; force4byteblocks: boolean=true; showmodulesonly: boolean=false; nosystemmodules:boolean=false; maxdepth:integer=0; referenceaddress: ptruint=0; referencename:string='');
{
ce_stacktrace will walk the provided stack trying to figure out functionnames ,passed strings and optional other data
esp must be aligned on a 4 byte boundary the first entry alignment but other entries will try to be forced to 4 byte alignment unless otherwise needed (double, string,...)

if eip is in a known function location this can help with the parameter naming (for the location of ebp)
esp is the first 4 byte of stack

nosystemmodules is only in effect when show moduleso only is enabled
}
var
  alignedstack: pbytearray;
  alignedstack32: PDwordArray absolute alignedstack;
  alignedstack64: Puint64Array absolute alignedstack;
  i: integer;
  Entries: integer;

  currentStackpos: ptruint;
  currentStackValue: ptruint;


  address: string;
  value: string;
  secondary: string;
  v: TVariableType;
  oldi: integer;

  originalesp: ptruint;
  offset: ptrint;
  offsetstring: string;
  pref: char;
begin
  if processhandler.SystemArchitecture=archX86 then
  begin
    if processhandler.is64bit then
      pref := 'r'
    else
      pref := 'e';

    originalesp := esp;
    i := esp mod processhandler.pointersize;
    if i > 0 then
    begin
      //unalligned
      alignedstack := stack + (processhandler.pointersize - i);
      Dec(sizeinbytes, processhandler.pointersize - i);
      Inc(esp, i);
    end
    else
      alignedstack := stack;

    Dec(sizeinbytes, sizeinbytes mod processhandler.pointersize);
    //so it's a full boundary

    Entries := sizeinbytes div processhandler.pointersize;

    i := 0;
    while i < entries do
    begin
      oldi := i;

      if referenceaddress = 0 then
        offsetstring := '(' + pref + 'sp+' + inttohex(esp - originalesp, 1) + ')'
      else
      begin
        offset := esp - referenceaddress;
        if offset < 0 then
          offsetstring := '(' + referencename + '-' + inttohex(-offset, 1) + ')'
        else
          offsetstring := '(' + referencename + '+' + inttohex(offset, 1) + ')';
      end;

      address := inttohex(esp, 8) + ' ' + offsetstring;


      if processhandler.is64bit then
      begin
        currentStackpos := ptruint(@alignedstack64[i]);
        Value := inttohex(alignedstack64[i], 16);
        currentStackValue := ptruint(alignedstack64[i]);
      end
      else
      begin
        currentStackpos := ptruint(@alignedstack32[i]);
        Value := inttohex(alignedstack32[i], 8);
        currentStackValue := ptruint(alignedstack32[i]);
      end;




      v := vtdword;

      //figure out what alignedstack[i] is.

      if showmodulesonly then
      begin
        if symhandler.inModule(currentStackValue) and
          ((not nosystemmodules) or (not symhandler.inSystemModule(currentStackValue))) then
          v := vtPointer
        else
        begin
          Inc(i);
          Inc(esp, (i - oldi) * processhandler.pointersize);
          continue; //skip
        end;
      end
      else
        v := FindTypeOfData(esp, pointer(currentstackpos), sizeinbytes -
          (i * processhandler.pointersize));

      if v in [vtbyte..vtQword] then
      begin
        //override into pointersize type
        if processhandler.is64bit then
          v := vtQword
        else
          v := vtDword;
      end;

      secondary := DataToString(@currentStackValue, processhandler.pointersize, v);
      //optional: find the lengts of a string and increase i with the number of stack elements used in the string
      Inc(i);


      trace.AddObject(address + ' - ' + Value + ' - ' + secondary, pointer(esp));
      if (maxdepth > 0) and (trace.Count >= maxdepth) then exit;

      Inc(esp, (i - oldi) * processhandler.pointersize);

    end;
  end;
end;


end.
